﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Net;

using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Security;
using System.IO;
using System.Collections;
using System.Diagnostics;

using King.Automation;
using King.Reflection;
using King.Declarative;
using King.Extensions;
using King.Text;
using King.Text.Xml;
using King.Azure.Rest;

// table /query tbl /select:foo /account:kingc  /key:zq1IBxib2lp8CK7/VS66zuYfkFnGaMcfRZS4COmffx9tBKabNf6ZFV17oqy5qsAwg1H3G/AWq34ZGF+/ppEZeg==
// /cert:7DB2018EC1AFDC8AAF978E7343345C1AC8B09C7E /id:c997d658-3d7b-4e19-a3fb-a4626f188a15
// /account:foo /key:aNnmtSipelMhFOI4u4W0l5aiGLVG11roTkoYuHidHT6yhreN8bFHn6b7AB/Euo/UDiOShFmFbTRb9qznd6oFng==
namespace az {

    public class Program {
        public static int Main(string[] args) {
            //Debugger.Break();
            return ComandConsoleApplication.EntryPoint(args);
        }
    }

    public abstract class AzureCommand : Command {

        public AzureCommand(params object[] arguments)
            : base(arguments) {
        }

        protected IEnumerable Format(HttpWebResponse response) {

            // read stream
            string resultText = null;
            using (var reader = new StreamReader(response.GetResponseStream()))
                resultText = reader.ReadToEnd();

            // format
            return new TextDoubleSpaced {
                new TextSingleSpaced {
                    from o in response.Headers.AllKeys
                    orderby o
                    select new TextJoin(": ") { o, response.Headers[o] },
                },

                string.IsNullOrEmpty(resultText) ? null : 
                    response.ContentType.ToLower().Contains("xml") ? (object)XElement.Parse(resultText) :
                    resultText
            };
        }
    }
    public abstract class StorageManagementCommand : AzureCommand {
        internal const string AnalyticsVersion = "analyticsVersion";
        internal const string DaysToRetain = "daysToRetain";

        internal StorageManagementCommand(params object[] arguments)
            : base(arguments) {
        }

        protected IEnumerable Format(WebException exception) {
            using (var reader = new StreamReader(exception.Response.GetResponseStream())) {
                var webResponse = (HttpWebResponse)exception.Response;
                var description = webResponse.StatusCode + ": " + webResponse.StatusDescription;

                var text = reader.ReadToEnd();

                if (text.Contains("DOCTYPE")) {
                    text = new StringReader(text).ReadLines().Skip(3).StringJoin();
                    text = text.Replace("<hr>", "").Replace("</HTML>", "");
                    var xml = XElement.Parse(text);
                    var code = xml.Descendants("h2").Single();
                    var message = xml.Descendants("p").Single();
                    throw new WebException(description + ": " + (string)code + ": " + (string)message, exception.InnerException);

                } else if (text.Contains("Message")) {
                    var xml = XElement.Parse(text);
                    var code = xml.Descendants("Code").Single();
                    var message = xml.Descendants("Message").Single();
                    var extra = new TextSingleSpaced { message.ElementsAfterSelf() }.ReadToEnd();
                    throw new WebException(description + ": " + (string)code + ": " + (string)message + Environment.NewLine + extra, exception.InnerException);
                }

                throw new WebException(description);
            }
        }
    }
    public abstract class SubscriptionManagementCommand : AzureCommand {
        private static readonly XNamespace XNamespace = (XNamespace)"http://schemas.microsoft.com/windowsazure";

        internal const string LocationDisplay = "geographicLocation";
        internal const string AffinityGroupDisplay = "affinityGroup";

        public SubscriptionManagementCommand(params object[] arguments)
            : base(arguments) {
        }

        protected IEnumerable Format(WebException exception) {
            using (var reader = new StreamReader(exception.Response.GetResponseStream())) {
                var xml = XElement.Parse(reader.ReadToEnd());
                var code = (string)xml.Element(XNamespace + "Code");
                var message = (string)xml.Element(XNamespace + "Message");
                throw new WebException(code + ": " + message, exception.InnerException);
            }
        }
    }

    public sealed class SubscriptionCredentials : CommandArgumentGroup {
        public static implicit operator AzureSubscriptionManagement(SubscriptionCredentials credentials) {
            return new AzureSubscriptionManagement(credentials.Id, credentials.Certificate);
        }

        internal const string ThumbprintEnvVar = "%AZURE_CERTIFICATE_THUMBPRINT%";
        internal const string ThumbprintDisplay = "thumbprint";

        internal const string IdEnvVar = "%AZURE_SUBSCRIPTION_ID%";
        internal const string IdDisplay = "subscriptionId";

        public SubscriptionCredentials(
            [CommandSwitch(IdDisplay, DefaultValue = IdEnvVar)]string id,
            [CommandSwitch(ThumbprintDisplay, DefaultValue = ThumbprintEnvVar)]string cert)
            : base(id, cert) {
        }

        private X509Certificate2 GetCertificate(string id, string thumbprintOrFile) {
            
            X509Certificate2 certificate = null;

            // .cer file
            if (File.Exists(thumbprintOrFile))
                certificate = (X509Certificate2)X509Certificate.CreateFromCertFile(thumbprintOrFile);

            // certificate store
            else {
                var x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);

                try {

                    try {
                        x509Store.Open(OpenFlags.ReadOnly);

                    } catch (Exception e) {
                        if (e is CryptographicException)
                            Console.WriteLine("Error: The store is unreadable.");
                        else if (e is SecurityException)
                            Console.WriteLine("Error: You don't have the required permission.");
                        else if (e is ArgumentException)
                            Console.WriteLine("Error: Invalid values in the store.");
                        else
                            throw;
                    }

                    var certificates = x509Store.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrFile, false);
                    if (certificates.Count != 1)
                        throw new Exception("Error: No certificate found containing thumbprint " + thumbprintOrFile);

                    certificate = certificates[0];

                } finally {
                    x509Store.Close();
                }
            }

            return certificate;
        }

        public string Id {
            get { return Get<string>(); }
        }
        public X509Certificate2 Certificate {
            get { return GetCertificate(Id, Get<string>("cert")); }
        }
    }
    public sealed class StorageCredentials : CommandArgumentGroup {
        public static implicit operator AzureTableManagment(StorageCredentials credentials) {
            return new AzureTableManagment(credentials.Account, credentials.Key);
        }
        public static implicit operator AzureBlobManagment(StorageCredentials credentials) {
            return new AzureBlobManagment(credentials.Account, credentials.Key);
        }
        public static implicit operator AzureQueueManagment(StorageCredentials credentials) {
            return new AzureQueueManagment(credentials.Account, credentials.Key);
        }

        internal const string AccountEnvVar = "%AZURE_STORAGE_ACCOUNT%";
        internal const string AccountDisplay = "accountName";

        internal const string KeyEnvVar = "%AZURE_STORAGE_KEY%";
        internal const string KeyDisplay = "storageKey";

        public StorageCredentials(
            [CommandSwitch(AccountDisplay, DefaultValue = AccountEnvVar)]string account,
            [CommandSwitch(KeyDisplay, DefaultValue = KeyEnvVar)]string key)
            : base(account, key) {
        }

        public string Account {
            get { return Get<string>(); }
        }
        public string Key {
            get { return Get<string>(); }
        }
    }
    public sealed class EntityId : CommandArgumentGroup {

        public EntityId(            
            string partitionKey,
            string rowKey)
            : base(partitionKey, rowKey) {
        }

        public string PartitionKey {
            get { return Get<string>(); }
        }
        public string RowKey {
            get { return Get<string>(); }
        }
    }

    /// <summary>
    /// Storage commands.
    /// </summary>
    [CommandGroup]
    public abstract class StorageCommand : SubscriptionManagementCommand {

        public StorageCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class StorageList : StorageCommand {

        [Command]
        public StorageList(
            SubscriptionCredentials credentials)
            : base(credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, AzureSubscriptionManagement credentials) {
            try {
                return Format(credentials.StorageAccounts());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class StorageDetails : StorageCommand {

        [Command]
        public StorageDetails(
            string name,
            SubscriptionCredentials credentials)
            : base(name, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, 
            string name, 
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials
                    .GetStorageAccount(name).Properties());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class StorageRegenerateKeys : StorageCommand {

        [Command]
        public StorageRegenerateKeys(
            string name,
            AzureStorageKeyType keyType,
            SubscriptionCredentials credentials)
            : base(name, keyType, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, 
            string name, AzureStorageKeyType keyType, 
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials
                    .GetStorageAccount(name).GetKey(keyType).Regenerate());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class StorageKeys : StorageCommand {

        [Command]
        public StorageKeys(
            string name,
            SubscriptionCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, 
            string name,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.GetStorageAccount(name).Keys());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class StorageDelete : StorageCommand {

        [Command]
        public StorageDelete(
            string name,
            SubscriptionCredentials credentials)
            : base(name, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, 
            string name,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials
                    .GetServiceAccount(name).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class StorageCreate : StorageCommand {

        [Command]
        public StorageCreate(
            string name,
            string label,
            string description = null,
            [CommandSwitch(LocationDisplay)]string location = null,
            [CommandSwitch(AffinityGroupDisplay)]string affinityGroup = null,
            SubscriptionCredentials credentials = null)
            : base(name, label, description, location, affinityGroup, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context,
            string name, string label, string description, string location, string affinityGroup,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials
                    .CreateStorageAccount(name, description, label, affinityGroup, location));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class StorageUpdate : StorageCommand {

        [Command]
        public StorageUpdate(
            string name,
            string label = null,
            string description = null,
            SubscriptionCredentials credentials = null)
            : base(name, label, description, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, string label, string description,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials
                    .GetStorageAccount(name).Update(description, label));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
        
    /// <summary>
    /// Service commands.
    /// </summary>
    [CommandGroup]
    public abstract class ServiceCommand : SubscriptionManagementCommand {

        public ServiceCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class ServiceList : ServiceCommand {

        [Command]
        public ServiceList(
            SubscriptionCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.ServiceAccounts());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ServiceCreate : ServiceCommand {

        [Command]
        public ServiceCreate(
            string name,
            string label,
            string description = null,
            [CommandSwitch(LocationDisplay)]string location = null,
            [CommandSwitch(AffinityGroupDisplay)]string affinityGroup = null,
            SubscriptionCredentials credentials = null)
            : base(name, label, description, location, affinityGroup, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context,
            string name, string label, string description, string location, string affinityGroup,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials
                    .CreateServiceAccount(name, label, description, location, affinityGroup));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ServiceDelete : ServiceCommand {

        [Command]
        public ServiceDelete(
            string name,
            SubscriptionCredentials credentials)
            : base(name, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, 
            string name,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.GetServiceAccount(name).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ServiceDetails : ServiceCommand {

        [Command]
        public ServiceDetails(
            string name,
            SubscriptionCredentials credentials)
            : base(name, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, 
            string name,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.GetServiceAccount(name).Properties());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Management Certificate Commands.
    /// </summary>
    [CommandGroup]
    public abstract class CertificateCommand : SubscriptionManagementCommand {

        public CertificateCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class CertificateList : CertificateCommand {

        [Command]
        public CertificateList(
            SubscriptionCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(DeclarationContext context, AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.Certificates());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class CertificateCreate : CertificateCommand {

        [Command]
        public CertificateCreate(
            string publicKey,
            string thumbprint,
            string data,
            SubscriptionCredentials credentials = null)
            : base(publicKey, thumbprint, data, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context,
            string publicKey, string thumbprint, string data,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.CreateCertificate(publicKey, thumbprint, data));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class CertificateDelete : CertificateCommand {

        [Command]
        public CertificateDelete(
            string name,
            SubscriptionCredentials credentials)
            : base(name, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context,
            string thumbprint,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.GetCertificate(thumbprint).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class CertificateDetails : CertificateCommand {

        [Command]
        public CertificateDetails(
            string thumbprint,
            SubscriptionCredentials credentials)
            : base(thumbprint, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context,
            string thumbprint,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.GetCertificate(thumbprint).Properties());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }


    /// <summary>
    /// Location commands.
    /// </summary>
    [CommandGroup]
    public abstract class LocationCommand : SubscriptionManagementCommand {

        public LocationCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class LocationList : LocationCommand {

        [Command(IsDefault = true)]
        public LocationList(
            SubscriptionCredentials credentials)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.Locations());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Subscription commands.
    /// </summary>
    [CommandGroup]
    public sealed class SubscriptionCommand : SubscriptionManagementCommand {

        [Command]
        public SubscriptionCommand(
            SubscriptionCredentials credentials)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.Subscriptions());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Log commands.
    /// </summary>
    [CommandGroup]
    public sealed class LogCommand : SubscriptionManagementCommand {

        [Command]
        public LogCommand(
            int seconds = 0,
            int minutes = 0,
            int hours = 0,
            int days = 0,
            string idFilter = null,
            AzureLogResult? resultFilter = null,
            SubscriptionCredentials credentials = null)
            : base(seconds, minutes, hours, days, idFilter, resultFilter, credentials) {
        }

        [Command]
        public LogCommand(
            DateTime startTime,
            DateTime endTime,
            string idFilter = null,
            AzureLogResult? resultFilter = null,
            SubscriptionCredentials credentials = null)
            : base(startTime, endTime, idFilter, resultFilter, credentials) {
        }

        private IEnumerable Execute(DeclarationContext context,
            int seconds, int minutes, int hours, int days,
            string idFilter, AzureLogResult? resultFiler,
            AzureSubscriptionManagement credentials) {

            var startTime = DateTime.UtcNow - new TimeSpan(days, hours, minutes, seconds);
            var endTime = DateTime.UtcNow;

            return Format(credentials.Logs(startTime, endTime, idFilter, resultFiler));
        }   

        private IEnumerable Execute(DeclarationContext context,
            DateTime startTime, DateTime endTime,
            string idFilter, AzureLogResult? resultFiler,
            AzureSubscriptionManagement credentials) {

            return Format(credentials.Logs(startTime, endTime, idFilter, resultFiler));
        }
    }

    /// <summary>
    /// Operating system commands.
    /// </summary>
    [CommandGroup]
    public abstract class OsCommand : SubscriptionManagementCommand {

        public OsCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class OsList : OsCommand {

        [Command(IsDefault = true)]
        public OsList(
            SubscriptionCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(DeclarationContext context,
            AzureSubscriptionManagement credentials) {

            try {
                var result = credentials.OperatingSystems();
                //return result.Count().ToString();
                throw new NotImplementedException();

            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Affinity group commands.
    /// </summary>
    [CommandGroup]
    public abstract class AffinityGroupCommand : SubscriptionManagementCommand {

        public AffinityGroupCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class AffinityGroupList : AffinityGroupCommand {

        [Command(IsDefault = true)]
        public AffinityGroupList(
            SubscriptionCredentials credentials)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials.AffinityGroups());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class AffinityGroupCreate : AffinityGroupCommand {

        public class Xml {

            public string Name { get; set; }
        }

        [Command]
        public AffinityGroupCreate(
            string name,
            string label,
            string description = null,
            string location = null,
            SubscriptionCredentials credentials = null)
            : base(name, label, description, location, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, string label, string description, string location,
            AzureSubscriptionManagement credentials) {

            try {
                return Format(credentials
                    .CreateAffinityGroup(name, label, description, location));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class AffinityGroupDetails : AffinityGroupCommand {

        public class Xml {

            public string Name { get; set; }
        }

        [Command]
        public AffinityGroupDetails(
            string name,
            SubscriptionCredentials credentials)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            AzureSubscriptionManagement credentials) {
            
            try {
                return Format(credentials
                    .GetAffinityGroup(name).Properties());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Table service commands.
    /// </summary>
    [CommandGroup]
    public abstract class TableServiceCommand : StorageManagementCommand {
        public TableServiceCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class TableServiceDetails : TableServiceCommand {

        [Command(IsDefault = true)]
        public TableServiceDetails(StorageCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureTableManagment credentials) {

            try {
                return Format(credentials.Properties());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class TableServiceLog : TableServiceCommand {

        [Command]
        public TableServiceLog(
            bool? enable = null,
            bool? read = null,
            bool? write = null,
            bool? delete = null,
            [CommandSwitch(DaysToRetain)]int? days = null,
            [CommandSwitch(AnalyticsVersion)]int? version = null,
            StorageCredentials credentials = null)
            : base(enable, read, write, delete, days, version, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            bool? enable, bool? read, bool? write, bool? delete, int? days, int? version,
            AzureTableManagment credentials) {

            try {
                var logging = new AzureTableLogging(
                    version: version,
                    read: read,
                    write: write,
                    delete: delete,
                    retentionEnable: enable,
                    retentionDays: days
                );
                return Format(credentials.Update(logging));

            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class TableServiceMetrics : TableServiceCommand {

        [Command]
        public TableServiceMetrics(
            bool? enable = null,
            bool? includeApis = null,
            [CommandSwitch(DaysToRetain)]int? days = null,
            [CommandSwitch(AnalyticsVersion)]int? version = null,
            StorageCredentials credentials = null)
            : base(enable, includeApis, days, version, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            bool? enable, bool? includeApis, int? days, int? version,
            AzureTableManagment credentials) {

            try {
                var metrics = new AzureTableMetrics(
                    version: version,
                    enable: enable,
                    retentionEnable: enable,
                    includeApi: includeApis,
                    retentionDays: days
                );
                return Format(credentials.Update(metrics: metrics));

            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Table commands.
    /// </summary>
    [CommandGroup]
    public abstract class TableCommand : StorageManagementCommand {

        public TableCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class TableCreate : TableCommand {

        [Command]
        public TableCreate(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            AzureTableManagment credentials) {

            try {
                return Format(credentials.CreateTable(name));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class TableDelete : TableCommand {

        [Command]
        public TableDelete(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            AzureTableManagment credentials) {

            try {
                return Format(credentials.GetTable(name).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class TableList : TableCommand {

        [Command(IsDefault = true)]
        public TableList(
            StorageCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureTableManagment credentials) {

            try {
                return Format(credentials.Tables());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class TableQuery : TableCommand {

        [Command]
        public TableQuery(
            string name,
            string filter = null,
            string select = null,
            int? top = null,
            StorageCredentials credentials = null)
            : base(name, filter, select, top, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            string filter,
            string select,
            int? top,
            AzureTableManagment credentials) {

            try {
                return Format(credentials.GetTable(name).Query(filter, select, top));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class TableInsert : TableCommand {

        [Command]
        public TableInsert(
            string name,
            EntityId entityId,
            StorageCredentials credentials = null,
            params CommandNameValue[] values)
            : base(name, entityId, credentials, values) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            EntityId entityId,
            AzureTableManagment credentials,
            CommandNameValue[] values) {

            try {
                return Format(
                    credentials.GetTable(name)
                        .Insert(entityId.PartitionKey, entityId.RowKey, values.Select(o =>
                            new AzureCell(o.Name, EntityCommand.ParseValue(o.Value))
                        ).ToArray()
                    )
                );
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Entity Commands.
    /// </summary>
    [CommandGroup]
    public abstract class EntityCommand : StorageManagementCommand {

        internal static object ParseValue(string value) {

            // guid
            Guid guid;
            if (Guid.TryParse(value, out guid))
                return guid;

            // date/time
            DateTime dateTime;
            if (DateTime.TryParse(value, out dateTime))
                return dateTime;

            // long
            var stringRegex = Regex.Match(value, @"^\s*'(?<string>.*)'\s*$");
            if (stringRegex.Success)
                return stringRegex.Get("string");

            // long
            var longRegex = Regex.Match(value, @"^\s*(?<long>\d*)(l|L)\s*$");
            if (longRegex.Success)
                return long.Parse(longRegex.Get("long"));

            // int
            var intRegex = Regex.Match(value, @"^\s*(?<int>\d*)\s*$");
            if (intRegex.Success)
                return int.Parse(intRegex.Get("int"));

            // double
            var doubleRegex = Regex.Match(value, @"^\s*(?<double>(\.\d)*)(d|D)\s*$");
            if (intRegex.Success)
                return double.Parse(doubleRegex.Get("double"));

            // bool
            var boolRegex = Regex.Match(value.ToLower(), @"^\s*(?<bool>(true)|(false))\s*$");
            if (boolRegex.Success)
                return double.Parse(boolRegex.Get("bool"));

            throw new NotSupportedException("Cannot deserialize '" + value + "' to a known type.");
        }

        public EntityCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class EntityUpdate : EntityCommand {

        [Command]
        public EntityUpdate(
            string name,
            EntityId entityId,
            string etag = null,
            StorageCredentials credentials = null,
            params CommandNameValue[] values)
            : base(name, entityId, etag, credentials, values) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            EntityId entityId,
            AzureIfUnchanged etag,
            AzureTableManagment credentials,
            CommandNameValue[] values) {

            try {
                return Format(
                    credentials
                        .GetTable(name)
                        .GetEntity(entityId.PartitionKey, entityId.RowKey)
                        .Update(etag ?? new AzureIfUnchanged(), 
                            values.Select(o => new AzureCell(o.Name, EntityCommand.ParseValue(o.Value))).ToArray()
                        )
                );
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class EntityMerge : EntityCommand {

        [Command]
        public EntityMerge(
            string name,
            EntityId entityId,
            string etag = null,
            StorageCredentials credentials = null,
            params CommandNameValue[] values)
            : base(name, entityId, etag, credentials, values) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            EntityId entityId,
            AzureIfUnchanged etag,
            AzureTableManagment credentials,
            CommandNameValue[] values) {

            try {
                return Format(
                    credentials
                        .GetTable(name)
                        .GetEntity(entityId.PartitionKey, entityId.RowKey)
                        .Merge(etag ?? new AzureIfUnchanged(), 
                            values.Select(o => new AzureCell(o.Name, EntityCommand.ParseValue(o.Value))).ToArray()
                        )
                );
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class EntityDelete : EntityCommand {

        [Command]
        public EntityDelete(
            string name,
            EntityId entityId,
            string etag = null,
            StorageCredentials credentials = null)
            : base(name, entityId, etag, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            EntityId entityId,
            AzureIfUnchanged etag,
            AzureTableManagment credentials) {

            try {
                return Format(
                    credentials
                        .GetTable(name)
                        .GetEntity(entityId.PartitionKey, entityId.RowKey)
                        .Delete(etag ?? new AzureIfUnchanged())
                );
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Container commands.
    /// </summary>
    [CommandGroup]
    public abstract class ContainerCommand : StorageManagementCommand {

        public ContainerCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class ContainerList : ContainerCommand {

        [Command(IsDefault = true)]
        public ContainerList(
            StorageCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureBlobManagment credentials) {

            try {
                return Format(credentials.Containers());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ContainerBlobs : ContainerCommand {

        [Command]
        public ContainerBlobs(
            string name,
            string prefix = null,
            string delimiter = null,
            string marker = null,
            int? maxResults = null,
            StorageCredentials credentials = null)
            : base(name, prefix, delimiter, marker, maxResults, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, string prefix, string delimiter, string marker, int? maxResults,
            AzureBlobManagment credentials) {

            try {
                return Format(credentials.GetContainer(name).Blobs(prefix, delimiter, marker, maxResults));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ContainerCreate : ContainerCommand {

        /// <summary>
        /// The Create Container operation creates a new container under the specified account. 
        /// If the container with the same name already exists, the operation fails.
        /// The container resource includes metadata and properties for that container. 
        /// It does not include a list of the blobs contained by the container.
        /// </summary>
        /// <param name="name">A name-value pair to associate with the container as metadata.</param>
        /// <param name="access">Specifies whether data in the container may be accessed publicly 
        /// and the level of access. If this header is not included in the request, container 
        /// data is private to the account owner.</param>
        [Command]
        public ContainerCreate(
            string name,
            AzureContainerAccess? access = null,
            StorageCredentials credentials = null)
            : base(name, access, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, AzureContainerAccess? access,
            AzureBlobManagment credentials) {
            
            try {
                return Format(credentials.CreateContainer(name, access));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ContainerDetails : ContainerCommand {

        [Command]
        public ContainerDetails(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            AzureBlobManagment credentials) {

            try {
                return Format(credentials.GetContainer(name).Properties());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ContainerMetadata : ContainerCommand {

        [Command]
        public ContainerMetadata(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            AzureBlobManagment credentials) {
                        
            try {
                return Format(credentials.GetContainer(name).Metadata());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ContainerAcl : ContainerCommand {

        [Command]
        public ContainerAcl(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            AzureBlobManagment credentials) {
            
            try {
                return Format(credentials.GetContainer(name).Acl());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class ContainerDelete : ContainerCommand {

        [Command]
        public ContainerDelete(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name,
            AzureBlobManagment credentials) {

            try {
                return Format(credentials.GetContainer(name).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Blob service commands.
    /// </summary>
    [CommandGroup]
    public abstract class BlobServiceCommand : StorageManagementCommand {
        public BlobServiceCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class BlobServiceDetails : BlobServiceCommand {

        [Command(IsDefault = true)]
        public BlobServiceDetails(
            StorageCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureBlobManagment credentials) {

            try {
                return Format(credentials.Properties());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class BlobServiceLog : BlobServiceCommand {

        [Command]
        public BlobServiceLog(
            bool? enable = null,
            bool? read = null,
            bool? write = null,
            bool? delete = null,
            [CommandSwitch(DaysToRetain)]int? days = null,
            [CommandSwitch(AnalyticsVersion)]int? version = null,
            StorageCredentials credentials = null)
            : base(enable, read, write, delete, days, version, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            bool? enable, bool? read, bool? write, bool? delete, int? days, int? version,
            AzureBlobManagment credentials) {

            try {
                var logging = new AzureBlobLogging(
                    version: version,
                    read: read,
                    write: write,
                    delete: delete,
                    retentionEnable: enable,
                    retentionDays: days
                );
                return Format(credentials.Update(logging));

            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class BlobServiceMetrics : BlobServiceCommand {

        [Command]
        public BlobServiceMetrics(
            bool? enable = null,
            bool? includeApis = null,
            [CommandSwitch(DaysToRetain)]int? days = null,
            [CommandSwitch(AnalyticsVersion)]int? version = null,
            StorageCredentials credentials = null)
            : base(enable, includeApis, days, version, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            bool? enable, bool? includeApis, int? days, int? version,
            AzureBlobManagment credentials) {

            try {
                var metrics = new AzureBlobMetrics(
                    version: version,
                    enable: enable,
                    retentionEnable: enable,
                    includeApi: includeApis,
                    retentionDays: days
                );
                return Format(credentials.Update(metrics: metrics));

            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Blob Commands.
    /// </summary>
    [CommandGroup]
    public abstract class BlobCommand : StorageManagementCommand {
        public BlobCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class BlobCreate : BlobCommand {

        [Command]
        public BlobCreate(
            string name,
            string container,
            [CommandTarget(Path =
                CommandPathInfo.Exists |
                CommandPathInfo.IsFile
             )] string file,
            string mimeType = "application/octet-stream",
            string language = null,
            string cacheControl = null,
            int? lease = null,
            StorageCredentials credentials = null)
            : base(name, container, file, mimeType, language, cacheControl, lease, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, string container, string file,
            string mimeType, string language, string cacheControl, int? lease,
            AzureBlobManagment credentials) {

            try {
                using (var stream = new FileStream(file, FileMode.Open))
                    return Format(credentials
                        .GetContainer(container).GetBlob(name)
                        .CreateOrUpdate(stream, (int)stream.Length, mimeType, language, cacheControl, lease)
                    );
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class BlobDetails : BlobCommand {

        [Command(IsDefault = true)]
        public BlobDetails(
            string name,
            string container,
            string since = null,
            StorageCredentials credentials = null)
            : base(name, container, since, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, string container,
            string since, AzureBlobManagment credentials) {

            try {
                AzureIfChanged ifChanged = null;
                if (since != null)
                    ifChanged = new AzureIfChanged(since);

                return Format(credentials.GetContainer(container).GetBlob(name).Properties(ifChanged));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class BlobGet : BlobCommand {

        [Command]
        public BlobGet(
            string name,
            string container,
            StorageCredentials credentials = null)
            : base(name, container, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, string container,
            AzureBlobManagment credentials) {
            
            try {
                return Format(credentials.GetContainer(container).GetBlob(name).Get());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class BlobDelete : BlobCommand {

        [Command]
        public BlobDelete(
            string name,
            string container,
            StorageCredentials credentials = null)
            : base(name, container, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, string container,
            AzureBlobManagment credentials) {

            try {
                return Format(credentials.GetContainer(container).GetBlob(name).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Queue Commands.
    /// </summary>
    [CommandGroup]
    public abstract class QueueCommand : StorageManagementCommand {
        public QueueCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class QueueList : QueueCommand {

        [Command(IsDefault = true)]
        public QueueList(
            StorageCredentials credentials = null)
            : base(credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            AzureQueueManagment credentials) {
            
            try {
                return Format(credentials.Queues());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class QueueCreate : QueueCommand {

        [Command]
        public QueueCreate(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, AzureQueueManagment credentials) {

            try {
                return Format(credentials.CreateQueue(name));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class QueueDelete : QueueCommand {

        [Command]
        public QueueDelete(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, AzureQueueManagment credentials) {

            try {
                return Format(credentials.GetQueue(name).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class QueueMetadata : QueueCommand {

        [Command]
        public QueueMetadata(
            string name,
            StorageCredentials credentials = null)
            : base(name, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string name, AzureQueueManagment credentials) {

            try {
                return Format(credentials.GetQueue(name).Metadata());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }

    /// <summary>
    /// Queue Commands.
    /// </summary>
    [CommandGroup]
    public abstract class MessageCommand : StorageManagementCommand {
        public MessageCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class MessagePut : MessageCommand {

        [Command]
        public MessagePut(
            string queue,
            string message,
            int? visibilityTimeout = null,
            int? timeToLive = null,
            StorageCredentials credentials = null)
            : base(queue, message, visibilityTimeout, timeToLive, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string queue, string message,
            int? visibilityTimeOut, int? timeToLive,
            AzureQueueManagment credentials) {

            var envelope = new XElement("QueueMessage",
                new XElement("MessageText", message)
            ).ToString();

            var bytes = Encoding.UTF8.GetBytes(envelope);

            using (var stream = new MemoryStream(bytes)) {
                yield return Format(credentials.GetQueue(queue)
                    .Put(stream, bytes.Length, visibilityTimeOut, timeToLive));
            }
        }
    }
    public sealed class MessageGet : MessageCommand {

        [Command]
        public MessageGet(
            string queue,
            int? count = null,
            int? lease = null,
            StorageCredentials credentials = null)
            : base(queue, count, lease, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string queue,
            int? count, int? lease,
            AzureQueueManagment credentials) {

            try {
                return Format(credentials.GetQueue(queue).Get(count, lease));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class MessagePeek : MessageCommand {

        [Command]
        public MessagePeek(
            string queue,
            int? count = null,
            StorageCredentials credentials = null)
            : base(queue, count, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string queue, int? count,
            AzureQueueManagment credentials) {
            
            try {
                return Format(credentials.GetQueue(queue).Peek(count));
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class MessageUpdate : MessageCommand {

        [Command]
        public MessageUpdate(
            string queue,
            string messageId,
            string popReceipt,
            string message,
            int? visibilityTimeOut,
            StorageCredentials credentials = null)
            : base(queue, messageId, popReceipt, message, visibilityTimeOut, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string queue, string messageId, string popRecipt, string message,
            int? visibilityTimeOut,
            AzureQueueManagment credentials) {

            var envelope = new XElement("QueueMessage",
                new XElement("MessageText", message)
            ).ToString();

            var bytes = Encoding.UTF8.GetBytes(envelope);

            using (var stream = new MemoryStream(bytes)) {
                yield return Format(credentials
                    .GetQueue(queue)
                    .GetMessage(messageId, popRecipt)
                    .Update(stream, bytes.Length, visibilityTimeOut));
            }
        }
    }
    public sealed class MessageDelete : MessageCommand {

        [Command]
        public MessageDelete(
            string queue,
            string messageId,
            string popRecipt,
            StorageCredentials credentials = null)
            : base(queue, messageId, popRecipt, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string queue, string messageId, string popRecipt,
            AzureQueueManagment credentials) {
                        
            try {
                return Format(credentials.GetQueue(queue).GetMessage(messageId, popRecipt).Delete());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
    public sealed class MessageClear : MessageCommand {

        [Command]
        public MessageClear(
            string queue,
            StorageCredentials credentials = null)
            : base(queue, credentials) {
        }

        private IEnumerable Execute(
            DeclarationContext context,
            string queue,
            AzureQueueManagment credentials) {

            try {
                return Format(credentials.GetQueue(queue).Clear());
            } catch (WebException e) {
                return Format(e);
            }
        }
    }
}

